import { NumArray, Scale9_Cfg } from "./Common";

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const resizeImg = require('resize-img');
const PNG = require('pngjs').PNG;
const { pinyin } = require('pinyin-pro');
const jpeg = require('jpeg-js');
const copydir = require('copy-dir');


export default class Utils
{
    /**
     * 获取文件夹下面的所有的文件(包括子文件夹)
     * @param dirPath 
     * @param outArr 
     * @param allPath 是否显示文件全路径
     */
    public static getAllFiles(dirPath:string,outArr:string[],allPath:boolean = true):void
    {
        fs.readdirSync(dirPath).forEach((name:string) =>{
            const filePath = path.join(dirPath, name);
            const stat = fs.statSync(filePath);
            if (stat.isFile()) 
            {
                outArr.push(allPath ? filePath : name);
            }
            else if (stat.isDirectory()) 
            {
                Utils.getAllFiles(filePath, outArr);
            }
        });
    }


    public static copyDir(srcPath:string,desPath:string)
    {
        copydir.sync(srcPath, desPath, {
            utimes: true,  // keep add time and modify time
            mode: true,    // keep file mode
            cover: true    // cover file when exists, default is true
        });
    }

    public static rmDir(dirPath:string,cb:Function|null = null)
    {
        let files = [];
        // console.log("开始删除")
        if (fs.existsSync(dirPath)) 
        {
            var count = 0
            var checkEnd =  () => {
                // console.log("进度", count)
                ++count == files.length && cb && cb()
            }
            files = fs.readdirSync(dirPath);
            files.forEach(function (file:string) 
            {
                let curPath = dirPath + "/" + file;
                if (fs.statSync(curPath).isDirectory()) 
                {
                    // console.log("遇到文件夹", curPath)
                    Utils.rmDir(curPath, checkEnd);
                } 
                else 
                {
                    fs.unlinkSync(curPath);
                    // console.log("删除文件完成", curPath)
                    checkEnd()
                }
            });
            // 如果删除文件夹为放置文件夹根目录  不执行删除
            fs.rmdirSync(dirPath);
            //为空时直接回调
            files.length === 0 && cb && cb()
        } 
        else 
        {
            cb && cb()
        }
    }


    /**
     * 是否是纹理资源
     * @param fileName 
     */
    public static isTextureFile(fileName:string):boolean
    {
        if(fileName.endsWith(".png") || fileName.endsWith(".jpg"))
        {
            return true;
        }
        return false;
    }


    /**
     * 根据key生成长度为len的md5码
     * @param key 
     * @param len 
     */
    public static getMd5(key:any,len:number|undefined=undefined):string
    {
        let hash:string = crypto.createHash('md5').update(key).digest('hex');
        len = len ? len : hash.length;
        let packId = hash.substr(0,len);
        return packId;
    }


    /**
     * 顔色转换
     * @param rgbaArray 
     * @param includingAlpha 
     */
    public static convertToHtmlColor(rgbaArray:any[], includingAlpha:boolean = false) 
    {
        var result = '#';
        var str;
        if (includingAlpha) 
        {
            str = rgbaArray[3].toString(16);
            if (str.length == 1)
                str = '0' + str;
            result += str;
        }
    
        for (var i = 0; i < 3; i++) 
        {
            str = rgbaArray[i].toString(16);
            if (str.length == 1)
                str = '0' + str;
            result += str;
        }
    
        return result;
    }

    /**
     * 初始化纹理数据
     * @param rowLen 
     * @param colLen 
     * @param imgBuff 
     * @param buffWidth 
     * @param offRow 
     * @param offCol 
     */
    private static initNumArr(rowLen:number,colLen:number, imgBuff:any,buffWidth:number,offRow:number,offCol:number):NumArray[]
    {
        let numArr:NumArray[] = [];
        for(let i=offRow ; i < rowLen; i++)
        {
            let baseIdx = i*buffWidth*4;
            let arr:any[] = [];
            numArr.push(arr);
            for(let j=offCol ; j < colLen; j++)
            {
                let p = baseIdx + j*4;
                arr.push(imgBuff[p],imgBuff[p+1],imgBuff[p+2],imgBuff[p+3]);
            }
        }
        return numArr;
    }

    /**
     * 格式化成png数据
     * @param numArr 
     */
    private static async format2PngData(numArr:NumArray[],outArr:Buffer[],idx:number)
    {
        return new Promise<void>((resolve:any, reject:any) =>{
            let height = numArr.length;
            if(!height)
            {
                resolve();
                return;
            }
            let width = numArr[0].length >> 2;
            let png = new PNG({
              filterType: 4,
              width: width,
              height: height
            });
            let rgbaArr:number[] = [];
            for(let i=0; i < height; i++)
            {
                rgbaArr = rgbaArr.concat(numArr[i]);
            }
            png.data = new Uint8Array(rgbaArr);
            let outBuff:number[] = [];
            png.on('data', (data:Buffer) =>{
                for(let i=0; i<data.length;i++)
                {
                    outBuff.push(data[i]);
                }
            });
            png.on('end', () =>{
                let buff = new Uint8Array(outBuff);
                outArr[idx] = Buffer.from(buff);
                resolve(buff);
            });
            png.pack();
        });
    }

    /**
     * 合并纹理数据
     * @param letfUpArr 
     * @param midUpArr 
     * @param rightUpArr 
     * @param midLeftArr 
     * @param midCentertArr 
     * @param midRightArr 
     * @param leftBottomArr 
     * @param midBottomArr 
     * @param rightBottomArr 
     * @param option 
     */
    private static mergeData(letfUpArr:NumArray[],midUpArr:NumArray[],rightUpArr:NumArray[],
        midLeftArr:NumArray[],midCentertArr:NumArray[],midRightArr:NumArray[],
        leftBottomArr:NumArray[],midBottomArr:NumArray[],rightBottomArr:NumArray[],
        option:Scale9_Cfg):Uint8Array
    {
        let rgbaArray:number[] = [];
        let rgbaArrFun = (arr:NumArray[],row:number,col:number):NumArray =>{
            col = col << 2;
            return [arr[row][col],arr[row][col+1],arr[row][col+2],arr[row][col+3]]
        }

        let isNullArry = (numArr:NumArray[]):boolean =>{
            return !numArr || numArr.length == 0;
        }

        for(let i=0 , n = option.up + option.bottom + option.middleHeight ; i < n ; i++)
        {
            for(let j=0 , m = option.left + option.right + option.middleWidth ; j < m ; j++)
            {
                if(i < option.up)
                {
                    if(j < option.left)
                    {
                        let row = i , col = j;
                        !isNullArry(letfUpArr) && rgbaArray.push(...rgbaArrFun(letfUpArr,row,col));
                    }
                    else if(j < m - option.right)
                    {
                        let row = i , col = j - option.left;
                        !isNullArry(midUpArr) && rgbaArray.push(...rgbaArrFun(midUpArr,row,col));
                    }
                    else
                    {
                        let row = i , col = j - option.left - option.middleWidth;
                        !isNullArry(rightUpArr) && rgbaArray.push(...rgbaArrFun(rightUpArr,row,col));
                    }
                }
                else if(i < n - option.bottom)
                {
                    if(j < option.left)
                    {
                        let row = i - option.up , col = j;
                        !isNullArry(midLeftArr) && rgbaArray.push(...rgbaArrFun(midLeftArr,row,col));
                    }
                    else if(j < m - option.right)
                    {
                        let row = i - option.up , col = j - option.left;
                        !isNullArry(midCentertArr) && rgbaArray.push(...rgbaArrFun(midCentertArr,row,col));
                    }
                    else
                    {
                        let row = i - option.up , col = j - option.left - option.middleWidth;
                        !isNullArry(midRightArr) && rgbaArray.push(...rgbaArrFun(midRightArr,row,col));
                    }
                }
                else
                {
                    if(j < option.left)
                    {
                        let row = i - option.up - option.middleHeight , col = j;
                        !isNullArry(leftBottomArr) && rgbaArray.push(...rgbaArrFun(leftBottomArr,row,col));
                    }
                    else if(j < m - option.right)
                    {
                        let row = i - option.up - option.middleHeight , col = j - option.left;
                        !isNullArry(midBottomArr) && rgbaArray.push(...rgbaArrFun(midBottomArr,row,col));
                    }
                    else
                    {
                        let row = i - option.up - option.middleHeight , col = j - option.left - option.middleWidth;
                        !isNullArry(rightBottomArr) && rgbaArray.push(...rgbaArrFun(rightBottomArr,row,col));
                    }
                }
            }
        }
        return new Uint8Array(rgbaArray);
    }

    /**
     * 设置img大小
     * @param imgData 源img数据
     * @param option 宽高
     */
    public static async formatScale9Buff(imgBuff:any,option:Scale9_Cfg):Promise<Uint8Array>
    {
        let letfUpArr       :NumArray[] = Utils.initNumArr(option.up,                   option.left,               imgBuff, option.width, 0,                           0);
        let midUpArr        :NumArray[] = Utils.initNumArr(option.up,                   option.width-option.right, imgBuff, option.width, 0,                           option.left);
        let rightUpArr      :NumArray[] = Utils.initNumArr(option.up,                   option.width,              imgBuff, option.width, 0,                           option.width-option.right);
        let midLeftArr      :NumArray[] = Utils.initNumArr(option.height-option.bottom, option.left,               imgBuff, option.width, option.up,                   0);
        let midCentertArr   :NumArray[] = Utils.initNumArr(option.height-option.bottom, option.width-option.right, imgBuff, option.width, option.up,                   option.left);
        let midRightArr     :NumArray[] = Utils.initNumArr(option.height-option.bottom, option.width,              imgBuff, option.width, option.up,                   option.width-option.right);
        let leftBottomArr   :NumArray[] = Utils.initNumArr(option.height,               option.left,               imgBuff, option.width, option.height-option.bottom, 0);
        let midBottomArr    :NumArray[] = Utils.initNumArr(option.height,               option.width-option.right, imgBuff, option.width, option.height-option.bottom, option.left);
        let rightBottomArr  :NumArray[] = Utils.initNumArr(option.height,               option.width,              imgBuff, option.width, option.height-option.bottom, option.width-option.right);
        
        return new Promise<Uint8Array>((resolve,_)=>{
            let pngDataArr:Buffer[] = [];
            let savePromises = [
                Utils.format2PngData(midUpArr, pngDataArr,0),
                Utils.format2PngData(midLeftArr,   pngDataArr,1),
                Utils.format2PngData(midBottomArr, pngDataArr,2),
                Utils.format2PngData(midRightArr,   pngDataArr,3),
                Utils.format2PngData(midCentertArr, pngDataArr,4),
            ];
    
            let reszie = async (numArr:NumArray[],w:number,h:number,uint8Buff:Uint8Array):Promise<void> => {
                return new Promise<void>(async (resolve, reject) =>{
                    if(!uint8Buff)
                    {
                        resolve();
                        return;
                    }
                    let data = await resizeImg(uint8Buff, {width: w,height: h});
                    new PNG().parse(data,(_:any,png:any)=>{
                        //把缩放的数据存入到对应的内存区域中
                        let buff:Buffer = png.data;
                        numArr.length = 0;
                        for(let i=0; i<png.height; i++)
                        {   
                            let baseIdx = i*png.width*4;
                            let arr:any[] = [];
                            numArr.push(arr);
                            for(let j=0; j<png.width; j++)
                            {
                                let p = baseIdx + j*4;
                                arr.push(buff[p],buff[p+1],buff[p+2],buff[p+3]);
                            }
                        }
                        resolve();
                    })
                });
            };
    
            let reszieAll = async () => {
                let resizePromises = [
                    reszie(midUpArr,  option.middleWidth, option.up,  pngDataArr[0]),
                    reszie(midLeftArr, option.left, option.middleHeight,   pngDataArr[1]),
                    reszie(midBottomArr, option.middleWidth, option.bottom, pngDataArr[2]),
                    reszie(midRightArr, option.right, option.middleHeight,  pngDataArr[3]),
                    reszie(midCentertArr,option.middleWidth,option.middleHeight,  pngDataArr[4]),
                ];
                Promise.all(resizePromises).then(() => {
                    let scale9Data = this.mergeData(letfUpArr,midUpArr,rightUpArr,
                        midLeftArr,midCentertArr,midRightArr,
                        leftBottomArr,midBottomArr,rightBottomArr,option);
                    let png = new PNG({
                        filterType: 4,
                        width :  option.left + option.middleWidth + option.right,
                        height : option.up + option.middleHeight + option.bottom
                    });
                    png.data = scale9Data;
                    // png.pack().pipe(fs.createWriteStream("test2.png"));
                    resolve(scale9Data);
                });
            }
            Promise.all(savePromises).then(reszieAll);
        });
    }


    public static chinese2pinyin(chineseStr:string):string
    {
        if(!chineseStr) return '';
        let res:string[] = pinyin(chineseStr, { toneType: 'none', type: 'array' });
        res = res.filter((val:string) => { return val.trim() });
        return res ? res.join('') : '';
    }

    public static isChineseStr(str:string):boolean
    {
        return escape(str).indexOf('%u') != -1;
    }

    public static formatFileName(name:string):string
    {
        let fileName = name;
        let suffixIdx = fileName.lastIndexOf('.');
        let ext = suffixIdx !=-1 ? fileName.substr(suffixIdx) : '';
        if(Utils.isChineseStr(name))
        {
            console.warn('文件名称有中文 可能会发生异常: '+name);
            fileName = Utils.chinese2pinyin(name).replace(' ','');
        }
        let nameStartIdx = fileName.indexOf('$')+1;
        for(let i=nameStartIdx , n = fileName.length; i < n ; i++)
        {
            let ch = fileName[i];
            let isLetterNum = (ch == '_') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9');
            if(!isLetterNum)
            {
                fileName = fileName.substr(nameStartIdx,i-nameStartIdx);
                break;
            }
        }
        //console.log(name,fileName + ext);
        return fileName + ext;
    }


    public static async getImgData(filePath:string):Promise<any>
    {
        return new Promise<any>((resolve,_)=>{
            let fileData = fs.readFileSync(filePath);
            let data:any;
            if(filePath.endsWith('.jpg'))
            {
                data =  jpeg.decode(fileData, {useTArray: true})
                resolve(data);
            }
            else
            {
                new PNG({ filterType: 4 }).parse(fileData, function (error:any, data:any) {
                    resolve(data);
                });
            }
        });
    }

    public static isFileExist(fileName:string):boolean
    {
        return fs.existsSync(fileName);
    }

    
    public static getValueByKey(key:string,str:string):string
    {
        let pattern = new RegExp(`(${key}(\s*)=(\s*)"([^"]+)")`, "g");
        let valTmpArr:string[] | null = str.match(pattern);
        if(valTmpArr)
        {
            let val:string = valTmpArr[0].split('"')[1];
            return val;
        }
        return "";
    }
}